package marmalius;

import java.sql.SQLException;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import marmalius.tools.Config;
import marmalius.tools.LogService;
import marmalius.item.ItemService;
import marmalius.item.ItemTemplate;
import marmalius.item.ItemType;
import marmalius.map.Region;
import marmalius.player.*;
import marmalius.quest.BaseQuest;
import marmalius.quest.QuestID;
import marmalius.tools.DBService;


public abstract class Player {
	protected Inventory inventory;
	protected Bank bank;
	protected Coordonne coord;
	protected List<Region> regions;
	protected GoalPoint goalPoint;
	protected boolean canDoGoal;
	private int playerSqlID;
	protected int dialogNpcID;
	protected int dialogID;
	protected boolean isMembers;
	private Map<QuestID, Integer> quests;
	
	protected String BonusName[] = {
		"@whi@Stab", "@gre@Slash", "@whi@Crush", "@gre@Magic", "@whi@Range",
		"@gre@Stab", "@whi@Slash", "@gre@Crush", "@whi@Magic", "@gre@Range",
		"@gre@Strength", "@whi@Prayer"
	};
	
	public void ForceCanDoGoal() { canDoGoal = true; }
	
	public int getDialogNpcID() { return dialogNpcID; }
	public void setDialogID(int dialogID) { this.dialogID = dialogID; }
	public int getDialogID() { return dialogID; }
	
	public void setQuestStatus(QuestID questID, int status) {
		if (!quests.containsKey(questID))
			quests.put(questID, status);
		quests.replace(questID, status);
	}
	
	public int getQuestStatus(QuestID questID) {
		if (!quests.containsKey(questID))
			return -1;
		return quests.get(questID);
	}
	
	public boolean isDialog() { return dialogNpcID != -1 || dialogID != -1; }
	
	public void resetDialog() {
		dialogNpcID = -1;
		dialogID = -1;
	}
	
	public int FocusPointX = -1, FocusPointY = -1;	
	public void TurnPlayerTo(int pointX, int pointY) {
		updateRequired = true;
		FocusPointX = 2 * pointX + 1;
		FocusPointY = 2 * pointY + 1;
	}		

	public void gfx110(int gfx) {
		mask100var1 = gfx;
		mask100var2 = 6553600;
		mask100update = true;
		updateRequired = true;
	}

	private void appendSetFocusDestination(stream str) {
		str.writeWordBigEndianA(FocusPointX);
		str.writeWordBigEndian(FocusPointY);
	}
	public int FightDelayz = 0;
	public boolean playerBeingAttacked;
	public boolean poisondmg = false;
	public int Stage1 = 0;
	public int Stage2 = 0;
	public int killing;
	public int NPCI = 0;
	public int NPC2 = 0;
	public boolean hasTask = false;
	public int SlayerNPC = 0;
	public int zamorakKills = 0;
	public int saradominKills = 0;
	public int guthixKills = 0;
	public int SlayNPC = 0;
	public int Amount2Slay = 0;
	public int combatTimer;
	public int donator = 0;
	public int Vet = 0;
	public int playerconfigSplitPrivate = 0; 
	public int oldHeight = 0;
	public boolean farmWeeds = false;
	public int Hopper = 0;
	public int tutdone = 0;
	public int tutprog = 0;
	public int hsMember = 0;
	public boolean eating = false;
	public int junk = 0;
	public int Wheat = 0;
	public int objectX = 0;
	public int objectY = 0;
	public int objectID = 0;
	public int funfight = 0;
	public int pestpoints = 0;
	public int Stage3 = 0;
	public int Stage4 = 0;
	public int Stage5 = 0;
	public int Stage6 = 0;
	public int npcAtt;
	public int playerAtt2;
	public int playerAtt;
	public int playerHits;

	public boolean Slayer2 = false;
	public boolean wasRunning = false;
	public int MaxObsticals = 500;
	public boolean StartedGnomeCourse = false;
	public boolean StartedBarbarianCourse = false;
	public boolean StartedWerewolfCourse = false;
	public boolean StartedWildernessCourse = false;
	public int LedgeTimer;
	public int ObsticalTimer;
	public boolean cant = false;
	public static int more2handed[] = {
		4726, 4068, 4503, 4508, 841, 843, 845, 847, 849, 851, 853, 855, 857, 859,
		4212, 4214, 4734, 4827, 7807, 4710, 4153, 7158, 4755, 4747, 4718, 861,
		6724, 4214, 7622, 3204};
	public int spellHitTimer;
	public boolean isInCombat;
	
	//TODO les variable commence par une minuscule ...
	/* WoodCutting */
	public int TreeId = 0;
	public int TreeX = 0;
	public int TreeY = 0;
	public int TreeTimer = 0;
	public int WCTimer = 0;
	public int playerAxe = -1;
	
	/* Mining */
	public int mineId = 0;
	public int mineX = 0;
	public int mineY = 0;
	public int mineTimer = 0;
	public int mTimer = 0;
	
	public boolean TreeSpawn = false;
	public int EntId;
	public boolean IsChopping = false;
	public int spellHitTimer2;
	public int RangeHitTimer;
	public int HitDiffDelay = 0;
	public int RangeHitTimer2;
	public int RangeHitTimer3;
	public int RangeHitTimer4;
	public int RhitDiff;
	public int RhitDiff2;
	public int prayerdectimer = -1;
	public boolean strprayon = false;
	public boolean attprayon = false;
	public boolean defprayon = false;
	public int strpraytype = 0;
	public int attpraytype = 0;
	public int defpraytype = 0;
	public int spd = -1;
	public int dpd = -1;
	public int apd = -1;
	public int pmpd = -1;
	public int prpd = -1;
	public int pm2pd = -1;
	public int retrib = -1;
	public int redem = -1;
	public int smite = -1;
	public int attdec = -1;
	public int strdec = -1;
	public int defdec = -1;
	public int fishsTimer = 0;
	public int fishsAmount = 0;
	public int fishmTimer = 0;
	public int fishmAmount = 0;
	public int fishlTimer = 0;
	public int fishlobbyAmount = 0;
	public int fishTimer = 0;
	public int isFollowID;
	public boolean isFollowing;
	public int summonedNPCS = 0;
	public int fishAmount = 0;
	public int sharkieTimer = 0;
	public int sharkieAmount = 0;
	public int aaTimer = 0;
	public int aaAmount = 0;
	public boolean InTradeArea = true;
	public int rakeTimer = 0;
	public boolean InFunSpot = true;
	public boolean InFunArea = true;
	public int rakeAmount = 0;
	public int bbTimer = 0;
	public int bbAmount = 0;
	public int ccTimer = 0;
	public int ccAmount = 0;
	public int ddTimer = 0;
	public int ddAmount = 0;
	public boolean castSpell, isStillSpell, castSpell2;
	public boolean RangeHit, RangeHit2, RangeHit3, RangeHit4;
	public int stillSpellGFX;
	public int rune1, rune1Am, rune2, rune2Am, rune3, rune3Am, rune4, rune4Am, spellXP;
	public int arenaSpellTimer;
	public boolean SaradominStrike, GuthixClaws, ZamorakFlames;
	public int walkdelay = 0;
	public int poisonTimer, poisonDelay;
	public boolean poisonDmg;
	public boolean onCwZammy = false;
	public boolean onCwSara = false;
	public boolean InArena = false;
	public boolean InArenas = false;
	public boolean InFun = false;
	public int friendslot = 0;
	public long friend64 = 0;

	public void freeze1() {
		walkdelay = 10;
	}

	public void freeze2() {
		walkdelay = 20;
	}

	public void freeze3() {
		walkdelay = 30;
	}

	public boolean checkBarricade(int X, int Y) {
		for (NPC npc : NPCHandler.getInstance().getNPCS()) {
			if (npc != null) {
				if (npc.npcType == 1532 || npc.npcType == 1533 || npc.npcType == 1534 || npc.npcType == 1535) {
					if (npc.absX == X && npc.absY == Y)
						return false;
				}
			}
		}
		return true;
	}
	public int LogoutDelay = 0;

	public void inCombat() {
		LogoutDelay = 20;
		FightDelayz = 20;
	}

	public void StopTradeScam() {
		client tp = (client)PlayerHandler.getInstance().getPlayer(tradeWith);
		client p = (client)PlayerHandler.getInstance().getPlayer(playerId);
		p.DeclineTrade();
		tradeOtherDeclined = false;
		p.RemoveAllWindows();
		tp.DeclineTrade();
		tp.tradeOtherDeclined = true;
		tp.RemoveAllWindows();
		System.out.println(playerName + " has disconnected during a trade.");
		System.out.println("Reseting both player's trades.");
	}

	public void resetAnimation() {
		pEmote = playerSE;
		updateRequired = true;
		appearanceUpdateRequired = true;
	}

	public void println_debug(String str) {
		System.out.println("[player-" + playerId + "]: " + str);
	}

	public void println(String str) {
		System.out.println("[player-" + playerId + "]: " + str);
	}
	public boolean newhptype = false;
	public int hptype = 0;
	
	public void setGoalPoint(GoalPoint goalPoint) { this.goalPoint = goalPoint; }

	// some remarks: one map region is 8x8
	// a 7-bit (i.e. 128) value thus ranges over 16 such regions
	// the active area of 104x104 is comprised of 13x13 such regions, i.e. from
	// the center region that is 6 regions in each direction (notice the magical 6
	// appearing also in map region arithmetics...)
	public int combat = 0;
	public Player(int playerID) {
		goalPoint = null;
		dialogNpcID = -1;
		dialogID = -1;
		canDoGoal = false;
		playerId = playerID;
		regions = new ArrayList<Region>();
		inventory = new Inventory((client)this);
		bank = new Bank((client)this);
		quests = new HashMap<QuestID, Integer>();
		
		playerFollow = new int[500];
		playerFollowID = -1;
		// playerName = "player"+playerId;
		playerRights = 0; // player rights

		for (int i = 0; i < playerLevel.length; i++) { // Setting Levels
			if (i == 3) {
				playerLevel[i] = 10;
				playerXP[i] = 1155;
			}
			else {
				playerLevel[i] = 1;
				playerXP[i] = 0;
			}
		}

		for (int i = 0; i < playerEquipment.length; i++) {
			playerEquipment[i] = -1;
			playerEquipmentN[i] = 0;
		}
		// Perhaps by quoting the above out it will stop resetting users before loading, so the user can't be reset,
		// also making the playerName equal the player id it stops it from saving unless a savedgame was loaded... 
		// tried, first thing i heard was "lmao i got reset for the 3rd time!"...ffs!
		// Setting Welcomescreen information
		Calendar cal = new GregorianCalendar();
		//int day = cal.get(Calendar.DAY_OF_MONTH);
		//int month = cal.get(Calendar.MONTH);
		//int year = cal.get(Calendar.YEAR);
		int calc = ((cal.get(Calendar.YEAR) * 10000) + (cal.get(Calendar.MONTH) * 100) + cal.get(Calendar.DAY_OF_MONTH));
		
		playerLastLogin = calc;
		playerLastConnect = "";
		playerIsMember = 1;
		playerMessages = 0;

		// Setting player standard look
		playerLook[0] = 45;
		playerLook[1] = 56;
		playerLook[2] = 61;
		playerLook[3] = 67;
		playerLook[4] = 70;
		playerLook[5] = 79;
		
		pHead = 7;
		pTorso = 25;
		pArms = 29;
		pHands = 35;
		pLegs = 39;
		pFeet = 44;
		pBeard = 14;
		// initial x and y coordinates of the player
		
		// the first call to updateThisPlayerMovement() will craft the proper initialization packet
		teleportToX = 3092;
		teleportToY = 3104;
		playerEquipment[playerAmulet] = 1731;
		playerEquipment[playerWeapon] = 1275;
		// client initially doesn't know those values yet
		heightLevel = 0;
		absX = absY = -1;
		mapRegionX = mapRegionY = -1;
		currentX = currentY = 0;
		coord = new Coordonne(-1, -1, -1);
		resetWalkingQueue();
	}

	void destruct() {
		playerListSize = 0;
		for (int i = 0; i < maxPlayerListSize; i++)
			playerList[i] = null;
		absX = absY = -1;
		mapRegionX = mapRegionY = -1;
		currentX = currentY = 0;
		resetWalkingQueue();
	}

	public boolean isNpc;
	public int npcId;
	public boolean initialized = false, disconnected = false, savefile = true;
	public boolean isActive = false;
	public boolean isKicked = false;
	public boolean CrackerMsg = false;
	public boolean CrackerForMe = false;
	public boolean IsGhost = false;

	public int actionTimer = 0;
	public int actionAmount = 0;
	public int blitzTimer = 0;
	public String actionName = "";

	public int theifTimer = 0;
	public int TakeMiscTimer = 0;

	public String connectedFrom = "";
	public String globalMessage = "";
	
	public int killcount = 0;
	public int deathcount = 0;
	public int magetp = 0;
	public int MageTP = 0;
	public int pkpoints = 0;
	public int PkPoints = 0;
	public String lastKill = "";

	public int AttackingOn = -1;
	public int playerIndex = -1;

	public int OptionObject = -1;

	public boolean Climbing = false;
	public int ClimbStage = -1;

	public int hiddenPoints; // number of places found ;)
	public int foundz[] = new int[100]; // used for secret places being found ;)

	public int[] clueItems = new int[28];
	public int[] clueItemsN = new int[28];

	public int ActionType = -1;
	public int destinationX = -1;
	public int destinationY = -1;
	public int destinationID = -1;
	public int destinationRange = 0;
	public boolean WalkingTo = false;

	public int duelWith = 0;
	public int duelStatus = -1; // 0 = Requesting duel, 1 = in duel screen, 2 = waiting for other player to accept, 3 = in duel, 4 = won
	public int duelChatStage = -1;
	public int duelChatTimer = -1;
	public int duelItems[] = new int[28];
	public int duelItemsN[] = new int[28];
	public int otherDuelItems[] = new int[28];
	public int otherDuelItemsN[] = new int[28];
	public boolean duelRule[] = new boolean[28];
	public boolean winDuel = false;
	public boolean startDuel = false;

	public int tradeRequest = 0;
	public int tradeDecline = 0;
	public int tradeWith = 0;
	public int tradeWaitingTime = 0;
	public int tradeStatus = 0;
	public boolean tradeUpdateOther = false;
	public static boolean tradeOtherDeclined = false;
	public int[] playerTItems = new int[28]; // player Trade Items
	public int[] playerTItemsN = new int[28];
	public int[] playerOTItems = new int[28]; // player Other Trade Items
	public int[] playerOTItemsN = new int[28];
	// Quest1 stuff @@@@@@@@@@@@@@@@@@@@@@@@@@@
	public int Guard = 0;
	public boolean Killedqueen = false;
	// end of quest1 stuff @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

	public boolean takeAsNote = false;
	
	public abstract void initialize();

	public abstract void update();

	public int playerId = -1; // -1 denotes world is full, otherwise this is the playerId
	// corresponds to the index in Player players[]

	protected String playerName; // name of the connecting client
	public String playerPass = null; // name of the connecting client
	public boolean isRunning2 = false;
	public boolean stoprunning = false;

	public int playerRights; // 0=normal player, 1=player mod, 2=real mod, 3=admin?

	public int maxItemAmount = 2147483647;

	public int[] playerItems = new int[28];
	public int[] playerItemsN = new int[28];
	
	public int getPlayerID() { return playerId; }

	// Default appearance
	public int pHead;
	public int pTorso;
	public int pArms;
	public int pHands;
	public int pLegs;
	public int pFeet;
	public int pBeard;
	public int pEmote = 0x328; // this being the original standing state
	public int pWalk = 0x333; // original walking animation
	public boolean apset = false;

	public int headIcon = 0;

	public int[] playerEquipment = new int[14];
	public int[] playerEquipmentN = new int[14];
	
	public int playerHat = 0;
	public int playerCape = 1;
	public int playerAmulet = 2;
	public int playerWeapon = 3;
	public int playerChest = 4;
	public int playerShield = 5;
	public int playerLegs = 7;
	public int playerHands = 9;
	public int playerFeet = 10;
	public int playerRing = 12;
	public int playerArrows = 13;

	public int playerAttack = 0;
	public int playerDefence = 1;
	public int playerStrength = 2;
	public int playerHitpoints = 3;
	public int playerRanged = 4;
	public int playerPrayer = 5;
	public int playerMagic = 6;
	public int playerCooking = 7;
	public int playerWoodcutting = 8;
	public int playerFletching = 9;
	public int playerFishing = 10;
	public int playerFiremaking = 11;
	public int playerCrafting = 12;
	public int playerSmithing = 13;
	public int playerMining = 14;
	public int playerHerblore = 15;
	public int playerAgility = 16;
	public int playerThieving = 17;
	public int playerSlayer = 18;
	public int playerFarming = 19;
	public int playerRunecrafting = 20;

	public int i = 0;

	public int[] playerLevel = new int[25];
	public int[] playerXP = new int[25];
	public int currentHealth = playerLevel[playerHitpoints];
	public int maxHealth = playerLevel[playerHitpoints];

	// the list of players currently seen by thisPlayer
	// this has to be remembered because the client will build up exactly the same list
	// and will be used on subsequent player movement update packets for efficiency
	public final static int maxPlayerListSize = PlayerHandler.getInstance().getMaxPlayers();
	public Player playerList[] = new Player[maxPlayerListSize];
	public int playerListSize = 0;
	// bit at position playerId is set to 1 incase player is currently in playerList
	public byte playerInListBitmap[] = new byte[(PlayerHandler.getInstance().getMaxPlayers() + 7) >> 3];

	// the list of npcs currently seen by thisPlayer
	// this has to be remembered because the client will build up exactly the same list
	// and will be used on subsequent player movement update packets for efficiency
	public final static int maxNPCListSize = NPCHandler.getInstance().getMaxNPC();
	public NPC npcList[] = new NPC[maxNPCListSize];
	public int npcListSize = 0;
	// bit at position npcId is set to 1 incase player is currently in playerList
	public byte npcInListBitmap[] = new byte[(NPCHandler.getInstance().getMaxNPC() + 7) >> 3];

	// supported within the packet adding new players are coordinates relative to thisPlayer
	// that are >= -16 and <= 15 (i.e. a signed 5-bit number)
	public boolean withinDistance(Player otherPlr) {
		if (heightLevel != otherPlr.heightLevel) {
			return false;
		}
		int deltaX = otherPlr.absX - absX, deltaY = otherPlr.absY - absY;
		return deltaX <= 15 && deltaX >= -16 && deltaY <= 15 && deltaY >= -16;
	}

	public boolean withinDistance(NPC npc) {
		if (heightLevel != npc.heightLevel) {
			return false;
		}
		if (npc.NeedRespawn == true) {
			return false;
		}
		int deltaX = npc.absX - absX, deltaY = npc.absY - absY;
		return deltaX <= 15 && deltaX >= -16 && deltaY <= 15 && deltaY >= -16;
	}

	public int mapRegionX, mapRegionY; // the map region the player is currently in
	public int absX, absY; // absolute x/y coordinates
	public int currentX, currentY; // relative x/y coordinates (to map region)
	// Note that mapRegionX*8+currentX yields absX
	public int heightLevel; // 0-3 supported by the client

	public boolean updateRequired = true; // set to true if, in general, updating for this player is required
	// i.e. this should be set to true whenever any of the other 
	// XXXUpdateRequired flags are set to true
	// Important: this does NOT include chatTextUpdateRequired!

	// walking related stuff - walking queue etc...
	public static final int walkingQueueSize = 50;
	public int walkingQueueX[] = new int[walkingQueueSize], walkingQueueY[] = new int[walkingQueueSize];
	public int wQueueReadPtr = 0;
	public int wQueueWritePtr = 0; // points to (first free) slot for writing to the queue
	public boolean isRunning = false;
	public int teleportToX = -1, teleportToY = -1; // contain absolute x/y coordinates of destination we want to teleport to

	public void gfx180(int gfx) {
		mask100var1 = gfx;
		mask100var2 = 11796480;
		mask100update = true;
		updateRequired = true;
	}

	public void gfx200(int gfx) {
		mask100var1 = gfx;
		mask100var2 = 13107200;
		mask100update = true;
		updateRequired = true;
	}

	public void gfx0(int gfx) {
		mask100var1 = gfx;
		mask100var2 = 65536;
		mask100update = true;
		updateRequired = true;
	}

	public void resetWalkingQueue() {
		wQueueReadPtr = wQueueWritePtr = 0;
		for (int j = 0; j < 50; j++) {
			walkingQueueX[j] = currentX;
			walkingQueueY[j] = currentY;
		}
	}

	public void addToWalkingQueue(int j, int k) {
		int l = (wQueueWritePtr + 1) % 50;
		if (l == wQueueWritePtr) {
			return;
		}
		else {
			walkingQueueX[wQueueWritePtr] = j;
			walkingQueueY[wQueueWritePtr] = k;
			wQueueWritePtr = l;
			return;
		}
	}
	
	public int NubDir;
	public int NubDir2;
	
	// returns 0-7 for next walking direction or -1, if we're not moving
	public int getNextWalkingDirection() {
		if (wQueueReadPtr == wQueueWritePtr)
			return -1;
		int j;
		do {
			j = misc.direction(currentX, currentY, walkingQueueX[wQueueReadPtr], walkingQueueY[wQueueReadPtr]);
			if (j == -1) {
				wQueueReadPtr = (wQueueReadPtr + 1) % 50;
			}
			else if ((j & 1) != 0) {
				println_debug("Invalid waypoint in walking queue!");
				resetWalkingQueue();
				return -1;
			}
		} while (j == -1 && wQueueReadPtr != wQueueWritePtr);
		if (j == -1) {
			return -1;
		}
		else {
			j >>= 1;
			currentX += misc.directionDeltaX[j];
			currentY += misc.directionDeltaY[j];
			absX += misc.directionDeltaX[j];
			absY += misc.directionDeltaY[j];
			coord.setAbsXYH(absX, absY, heightLevel);
			return j;
		}
	}

	// calculates directions of player movement, or the new coordinates when teleporting
	public boolean didTeleport = false; // set to true if char did teleport in this cycle
	public boolean mapRegionDidChange = false;
	public int dir1 = -1, dir2 = -1; // direction char is going in this cycle
	public int poimiX = 0, poimiY = 0;
	public void getNextPlayerMovement() {
		mapRegionDidChange = false;
		didTeleport = false;
		dir1 = dir2 = -1;
		if (teleportToX != -1 && teleportToY != -1) {
			mapRegionDidChange = true;
			if (mapRegionX != -1 && mapRegionY != -1) {
				int j = teleportToX - mapRegionX * 8;
				int k = teleportToY - mapRegionY * 8;
				if (j >= 16 && j < 88 && k >= 16 && k < 88) {
					mapRegionDidChange = false;
				}
			}
			if (mapRegionDidChange) {
				mapRegionX = (teleportToX >> 3) - 6;
				mapRegionY = (teleportToY >> 3) - 6;
			}
			currentX = teleportToX - 8 * mapRegionX;
			currentY = teleportToY - 8 * mapRegionY;
			
			absX = teleportToX;
			absY = teleportToY;
			coord.setAbsXYH(absX, absY, heightLevel);
			if (mapRegionDidChange)
				coord.updateMapRegion();
			resetWalkingQueue();
			teleportToX = teleportToY = -1;
			didTeleport = true;
		}
		else {
			dir1 = getNextWalkingDirection();
			if (dir1 == -1)
				return;
			if (isRunning)
				dir2 = getNextWalkingDirection();
			
			coord.updateMapRegion2();
			
			byte byte0 = 0;
			byte byte1 = 0;

			if (currentX < 16) {
				byte0 = 32;
				mapRegionX -= 4;
				mapRegionDidChange = true;
			}
			else if (currentX >= 88) {
				byte0 = -32;
				mapRegionX += 4;
				mapRegionDidChange = true;
			}
			if (currentY < 16) {
				byte1 = 32;
				mapRegionY -= 4;
				mapRegionDidChange = true;
			}
			else if (currentY >= 88) {
				byte1 = -32;
				mapRegionY += 4;
				mapRegionDidChange = true;
			}
			if (mapRegionDidChange) { 
				currentX += byte0;
				currentY += byte1;
			//if (coord.updateMapRegion2()) { //remove 2 up line (currentX & Y +=)
				for (int l = 0; l < 50; l++) {
					walkingQueueX[l] += byte0;
					walkingQueueY[l] += byte1;
				}
			}
		}
	}

	// handles anything related to character position, i.e. walking,running and teleportation
	// applies only to the char and the client which is playing it
	public void updateThisPlayerMovement(stream str) {
		if (mapRegionDidChange) {
			str.createFrame(73);
			str.writeWordA(mapRegionX + 6); // for some reason the client substracts 6 from those values
			str.writeWord(mapRegionY + 6);
		}
		if (didTeleport == true) {
			str.createFrameVarSizeWord(81);
			str.initBitAccess();
			str.writeBits(1, 1);
			str.writeBits(2, 3); // updateType
			str.writeBits(2, heightLevel);
			str.writeBits(1, 1); // set to true, if discarding (clientside) walking queue
			str.writeBits(1, (updateRequired) ? 1 : 0);
			str.writeBits(7, currentY);
			str.writeBits(7, currentX);
			if (IsDead == true && IsDeadTimer == true
					&& IsDeadTeleporting == true) {
				IsDead = false;
				IsDeadTimer = false;
				SafeMyLife = false;
				IsUsingSkill = false;
			}
			return;
		}
		if (dir1 == -1) {
			// don't have to update the character position, because we're just standing
			str.createFrameVarSizeWord(81);
			str.initBitAccess();
			if (updateRequired) {
				// tell client there's an update block appended at the end
				str.writeBits(1, 1);
				str.writeBits(2, 0);
			}
			else {
				str.writeBits(1, 0);
			}
			if (DirectionCount < 50) {
				DirectionCount++;
			}
		}
		else {
			DirectionCount = 0;
			str.createFrameVarSizeWord(81);
			str.initBitAccess();
			str.writeBits(1, 1);
			if (dir2 == -1) {
				// send "walking packet"
				str.writeBits(2, 1); // updateType
				str.writeBits(3, misc.xlateDirectionToClient[dir1]);
				if (updateRequired) {
					str.writeBits(1, 1);
				} // tell client there's an update block appended at the end
				else {
					str.writeBits(1, 0);
				}
			}
			else {
				// send "running packet"
				str.writeBits(2, 2); // updateType
				str.writeBits(3, misc.xlateDirectionToClient[dir1]);
				str.writeBits(3, misc.xlateDirectionToClient[dir2]);
				if (updateRequired) {
					str.writeBits(1, 1);
				} // tell client there's an update block appended at the end
				else {
					str.writeBits(1, 0);
				}
				if (playerEnergy > 0) {
					playerEnergy -= 1;
				}
				else {
					isRunning2 = false;
					stoprunning = true;
				}
			} 
		}
	}

	// handles anything related to character position basically walking, running and standing
	// applies to only to "non-thisPlayer" characters
	public void updatePlayerMovement(stream str) {
		if (dir1 == -1) {
			// don't have to update the character position, because the char is just standing
			if (updateRequired || chatTextUpdateRequired) {
				// tell client there's an update block appended at the end
				str.writeBits(1, 1);
				str.writeBits(2, 0);
			}
			else {
				str.writeBits(1, 0);
			}
		}
		else if (dir2 == -1) {
			// send "walking packet"
			str.writeBits(1, 1);
			str.writeBits(2, 1);
			str.writeBits(3, misc.xlateDirectionToClient[dir1]);
			str.writeBits(1, (updateRequired || chatTextUpdateRequired) ? 1 : 0);
		}
		else {
			// send "running packet"
			str.writeBits(1, 1);
			str.writeBits(2, 2);
			str.writeBits(3, misc.xlateDirectionToClient[dir1]);
			str.writeBits(3, misc.xlateDirectionToClient[dir2]);
			str.writeBits(1, (updateRequired || chatTextUpdateRequired) ? 1 : 0);
		}
	}

	public static client client = null;
	// client = new client();
	public boolean dropsitem = false;
	public void removeequipped() {
		dropsitem = true;
	}

	public void setPlrAnimation(int i) {
		pEmote = i;
		updateRequired = true;
		appearanceUpdateRequired = true;
	}

	public int GetPlrBlockAnim(int id) {
		if (id == 4755) {// veracs flail
			return 2063;
		}
		if (id == 4153) {// maul
			return 1666;
		}
		else {
			return 1834;
		}
	}
	// a bitmap of players that we want to keep track of whether char appearance has changed so
	// we know if we have to transmit those or can make use of the cached char appearances in the client
	public byte cachedPropertiesBitmap[] = new byte[(PlayerHandler.getInstance().getMaxPlayers() + 7) >> 3];

	public void addNewNPC(NPC npc, stream str, stream updateBlock) {
		int id = npc.npcId;
		npcInListBitmap[id >> 3] |= 1 << (id & 7); // set the flag
		npcList[npcListSize++] = npc;
		str.writeBits(14, id); // client doesn't seem to like id=0
		
		int z = npc.absY - absY;
		if (z < 0) {
			z += 32;
		}
		str.writeBits(5, z); // y coordinate relative to thisPlayer
		z = npc.absX - absX;
		if (z < 0) {
			z += 32;
		}
		str.writeBits(5, z); // x coordinate relative to thisPlayer
		str.writeBits(1, 0); // something??
		str.writeBits(12, npc.npcType);
		
		boolean savedUpdateRequired = npc.updateRequired;
		npc.updateRequired = true;
		npc.appendNPCUpdateBlock(updateBlock);
		npc.updateRequired = savedUpdateRequired;	
		str.writeBits(1, 1); // update required
	}
		
	public void addNewPlayer(Player plr, stream str, stream updateBlock) {
		int id = plr.playerId;
		playerInListBitmap[id >> 3] |= 1 << (id & 7); // set the flag
		playerList[playerListSize++] = plr;
		str.writeBits(11, id); // client doesn't seem to like id=0
		// TODO: properly implement the character appearance handling
		// send this everytime for now and don't make use of the cached ones in client
		str.writeBits(1, 1); // set to true, if player definitions follow below
		boolean savedFlag = plr.appearanceUpdateRequired;
		boolean savedUpdateRequired = plr.updateRequired;
		plr.appearanceUpdateRequired = true;
		plr.updateRequired = true;
		plr.appendPlayerUpdateBlock(updateBlock);
		plr.appearanceUpdateRequired = savedFlag;
		plr.updateRequired = savedUpdateRequired;
		str.writeBits(1, 1); // set to true, if we want to discard the (clientside) walking queue
		// no idea what this might be useful for yet
		int z = plr.absY - absY;
		if (z < 0) {
			z += 32;
		}
		str.writeBits(5, z); // y coordinate relative to thisPlayer
		z = plr.absX - absX;
		if (z < 0) {
			z += 32;
		}
		str.writeBits(5, z); // x coordinate relative to thisPlayer
	}
	
	// player appearance related stuff
	protected boolean appearanceUpdateRequired = true; // set to true if the player appearance wasn't synchronized
	// with the clients yet or changed recently
	
	protected static stream playerProps;
	static {
		playerProps = new stream(new byte[100]);
	}
	protected void appendPlayerAppearance(stream str) {
		playerProps.currentOffset = 0;
		
		// TODO: yet some things to figure out on this block + properly implement this
		playerProps.writeByte(playerLook[0]); // player sex. 0=Male and 1=Female
		// playerProps.writeByte(1 & 1 >> 2);		// playerStatusMask - skull, prayers etc alkup 0
		playerProps.writeByte(headIcon);
		
		// defining the character shape - 12 slots following - 0 denotes a null entry and just a byte is used
		// slot 0,8,11,1 is head part - missing additional things are beard and eyepatch like things either 11 or 1
		// cape, apron, amulet... the remaining things...
		
		if (isNpc == false) {
			if (playerEquipment[playerHat] > 1) {
				playerProps.writeWord(0x200 + playerEquipment[playerHat]);
			}
			else {
				playerProps.writeByte(0);
			}
			if (playerEquipment[playerCape] > 1) {
				playerProps.writeWord(0x200 + playerEquipment[playerCape]);
			}
			else {
				playerProps.writeByte(0);
			}
			if (playerEquipment[playerAmulet] > 1) {
				playerProps.writeWord(0x200 + playerEquipment[playerAmulet]);
			}
			else {
				playerProps.writeByte(0);
			}
			if (playerEquipment[playerWeapon] > 1 && TreeId == 0) {
				playerProps.writeWord(0x200 + playerEquipment[playerWeapon]);
			}
			else if (playerAxe > 0 && TreeId == 0) {
				playerProps.writeWord(0x200 + playerAxe);
			}
			else {
				playerProps.writeByte(0);
			}
			if (playerEquipment[playerChest] > 1) {
				playerProps.writeWord(0x200 + playerEquipment[playerChest]);
			}
			else {
				playerProps.writeWord(0x100 + pTorso);
			}
			if (playerEquipment[playerShield] > 1 && TreeId == 0) {
				playerProps.writeWord(0x200 + playerEquipment[playerShield]);
			}
			else {
				playerProps.writeByte(0);
			}
			if (!Item0.isPlate(playerEquipment[playerChest])) {
				playerProps.writeWord(0x100 + pArms);
			}
			else {
				playerProps.writeByte(0);
			}
			if (playerEquipment[playerLegs] > 1) {
				playerProps.writeWord(0x200 + playerEquipment[playerLegs]);
			} else {
				playerProps.writeWord(0x100 + pLegs);
			}
			if (!Item0.isFullHelm(playerEquipment[playerHat]) && !Item0.isFullMask(playerEquipment[playerHat])) {
				playerProps.writeWord(0x100 + pHead); // head
			}
			else {
				playerProps.writeByte(0);
			}
			if (playerEquipment[playerHands] > 1) {
				playerProps.writeWord(0x200 + playerEquipment[playerHands]);
			}
			else {
				playerProps.writeWord(0x100 + pHands);
			}
			if (playerEquipment[playerFeet] > 1) {
				playerProps.writeWord(0x200 + playerEquipment[playerFeet]);
			}
			else {
				playerProps.writeWord(0x100 + pFeet);
			}
			if (!Item0.isFullMask(playerEquipment[playerHat]) && (playerLook[0] != 1)) {
				playerProps.writeWord(0x100 + pBeard);
			} // Beard
			else {
				playerProps.writeByte(0);
			}
		}
		else {
			playerProps.writeWord(-1);
			playerProps.writeWord(npcId);
		}
		// array of 5 bytes defining the colors
		playerProps.writeByte(playerLook[1]); // hair color
		playerProps.writeByte(playerLook[2]); // torso color.
		playerProps.writeByte(playerLook[3]); // leg color
		playerProps.writeByte(playerLook[4]); // feet color
		playerProps.writeByte(playerLook[5]); // skin color (0-6)
		playerProps.writeWord(pEmote); // standAnimIndex
		playerProps.writeWord(playerSET); // standTurnAnimIndex
		playerProps.writeWord(playerSEW); // walkAnimIndex
		playerProps.writeWord(playerSET180); // turn180AnimIndex
		playerProps.writeWord(playerSET90WC); // turn90CWAnimIndex
		playerProps.writeWord(playerSET90CCW); // turn90CCWAnimIndex
		playerProps.writeWord(playerSER); // runAnimIndex
		playerProps.writeQWord(misc.playerNameToInt64(playerName));
		// Stat fix, combat decreases when your hp or any of these skills get lowerd, this fixes that problem.
		
		int mag = (int) ((double) (getLevelForXP(playerXP[4])) * 1.5);
		int ran = (int) ((double) (getLevelForXP(playerXP[6])) * 1.5);
		int attstr = (int) ((double) (getLevelForXP(playerXP[0])) + (double) (getLevelForXP(playerXP[2])));
		
		int combatLevel = 0;
		
		if (ran > attstr) {
			combatLevel = (int) (((double) (getLevelForXP(playerXP[1])) * 0.25)
				+ ((double) (getLevelForXP(playerXP[3])) * 0.25)
				+ ((double) (getLevelForXP(playerXP[5])) * 0.125)
				+ ((double) (getLevelForXP(playerXP[6])) * 0.4875));
		}
		else if (mag > attstr) {
			combatLevel = (int) (((double) (getLevelForXP(playerXP[1])) * 0.25)
				+ ((double) (getLevelForXP(playerXP[3])) * 0.25)
				+ ((double) (getLevelForXP(playerXP[5])) * 0.125)
				+ ((double) (getLevelForXP(playerXP[4])) * 0.4875));
		}
		else {
			combatLevel = (int) (((double) (getLevelForXP(playerXP[1])) * 0.25)
				+ ((double) (getLevelForXP(playerXP[3])) * 0.25)
				+ ((double) (getLevelForXP(playerXP[5])) * 0.125)
				+ ((double) (getLevelForXP(playerXP[0])) * 0.325)
				+ ((double) (getLevelForXP(playerXP[2])) * 0.325));
		}

		combat = combatLevel;
		playerProps.writeByte(combatLevel); // combat level
		PlayerCBLVL = combatLevel;
		playerProps.writeWord(0); // incase != 0, writes skill-%d
		str.writeByteC(playerProps.currentOffset); // size of player appearance block
		str.writeBytes(playerProps.buffer, playerProps.currentOffset, 0);
	}
	public int PlayerCBLVL;

	protected boolean chatTextUpdateRequired = false;
	protected byte chatText[] = new byte[4096], chatTextSize = 0;
	protected int chatTextEffects = 0, chatTextColor = 0;
	protected void appendPlayerChatText(stream str) {
		str.writeWordBigEndian(((chatTextColor & 0xFF) << 8) + (chatTextEffects & 0xFF));
		str.writeByte(playerRights);
		str.writeByteC(chatTextSize); // no more than 256 bytes!!!
		str.writeBytes_reverse(chatText, chatTextSize, 0);
	}
	public boolean update1Required = false;
	public int update1_1 = 0;
	public int update1_2 = 0;
	protected void appendUpdate1(stream str) {
		str.writeWordBigEndian(update1_1);
		str.writeByteC(update1_2);
	}

	public void appendPlayerUpdateBlock(stream str) {
		if (!updateRequired && !chatTextUpdateRequired) {
			return;
		}		// nothing required
		int updateMask = 0;
		if (mask400update) {
			updateMask |= 0x400;
		} // Xerozcheez: Very interesting update mask!
		if (mask100update) {
			updateMask |= 0x100;
		} // Xerozcheez: Graphics on player update mask
		if (animationRequest != -1) {
			updateMask |= 8;
		}
		if (string4UpdateRequired) {
			updateMask |= 4;
		}
		if (chatTextUpdateRequired) {
			updateMask |= 0x80;
		}
		if (faceNPCupdate) {
			updateMask |= 1;
		}
		if (FocusPointX != -1) {
			updateMask |= 2;
		}
		if (appearanceUpdateRequired) {
			updateMask |= 0x10;
		}
		if (hitUpdateRequired) {
			updateMask |= 0x200;
		}
		if (dirUpdateRequired) {
			updateMask |= 0x40;
		}
		if (dirUpdate2Required) {
			updateMask |= 2;
		}
		if (animationRequest != -1) {
			updateMask |= 8;
		}
		if (updateMask >= 0x100) {
			// byte isn't sufficient
			updateMask |= 0x40; // indication for the client that updateMask is stored in a word
			str.writeByte(updateMask & 0xFF);
			str.writeByte(updateMask >> 8);
		} else {
			str.writeByte(updateMask);
		}
		// now writing the various update blocks itself - note that their order crucial
		if (mask400update) {
			appendMask400Update(str);
		} // Xerozcheez: Very interesting update mask!
		if (mask100update) {
			appendMask100Update(str);
		} // Xerozcheez: Graphics on player update mask
		if (animationRequest != -1) {
			appendAnimationRequest(str);
		}
		if (string4UpdateRequired) {
			appendString4(str);
		}
		if (chatTextUpdateRequired) {
			appendPlayerChatText(str);
		}
		if (faceNPCupdate) {
			appendFaceNPCUpdate(str);
		}
		if (appearanceUpdateRequired) {
			appendPlayerAppearance(str);
		}
		if (hitUpdateRequired) {
			appendHitUpdate(str);
		}
		if (dirUpdateRequired) {
			appendDirUpdate(str);
		}
		if (dirUpdate2Required) {
			appendDirUpdate2(str);
		}
		if (FocusPointX != -1) {
			appendSetFocusDestination(str);
		}
		// if(update1Required) appendEmotionUpdate(str); // tested, thought it was animation, and i was right! whitefang got it working for me :D - xerozcheez
		// if(mask1update) appendMask1Update(str); // a strange update mask, only one var (_) - xerozcheez
		// if(mask100update) appendMask100Update(str); // very interesting update mask! - xerozcheez
		// if(animationRequest != -1) appendAnimationRequest(str);
		// TODO: add the various other update blocks
	}

	public void clearUpdateFlags() { // Xerozcheez: ORDER IS CRUCIAL HERE TOO (although it's different order I think LOL) :|
		updateRequired = false;
		// animationRequest = -1;
		string4UpdateRequired = false;
		chatTextUpdateRequired = false;
		appearanceUpdateRequired = false;
		hitUpdateRequired = false;
		dirUpdateRequired = false;
		FocusPointX = FocusPointY = -1;
		animationRequest = -1;
		dirUpdate2Required = false;
		faceNPCupdate = false;
		faceNPC = 65535;
		mask100update = false;
		update1Required = false;
		IsStair = false;
	}

	protected static int newWalkCmdX[] = new int[50];
	protected static int newWalkCmdY[] = new int[50];
	protected static int newWalkCmdSteps = 0;
	protected static boolean newWalkCmdIsRunning = false;
	protected static int travelBackX[] = new int[50];
	protected static int travelBackY[] = new int[50];
	protected static int numTravelBackSteps = 0;
	protected static int tmpNWCX[] = new int[50];
	protected static int tmpNWCY[] = new int[50];

	public void preProcessing() {
		newWalkCmdSteps = 0;
	}

	// is being called regularily every 500ms - do any automatic player actions herein
	public abstract boolean process(int time);

	public void postProcessing() {
		if (newWalkCmdSteps > 0) {
			int OldcurrentX = currentX;
			int OldcurrentY = currentY;

			for (i = 0; i < playerFollow.length; i++) {
				if (playerFollow[i] != -1 && following == true) {
					PlayerHandler.getInstance().getPlayer(playerFollow[i]).newWalkCmdSteps = (newWalkCmdSteps
							+ 1);
					for (int j = 0; j < newWalkCmdSteps; j++) {
						PlayerHandler.getInstance().getPlayer(playerFollow[i]).newWalkCmdX[(j + 1)] = newWalkCmdX[j];
						PlayerHandler.getInstance().getPlayer(playerFollow[i]).newWalkCmdY[(j + 1)] = newWalkCmdY[j];
					}
					PlayerHandler.getInstance().getPlayer(playerFollow[i]).newWalkCmdX[0] = OldcurrentX;
					PlayerHandler.getInstance().getPlayer(playerFollow[i]).newWalkCmdY[0] = OldcurrentY;
					PlayerHandler.getInstance().getPlayer(playerFollow[i]).poimiX = OldcurrentX;
					PlayerHandler.getInstance().getPlayer(playerFollow[i]).poimiY = OldcurrentY;
				}
				int firstX = newWalkCmdX[0], firstY = newWalkCmdY[0];
				int lastDir = 0;
				boolean found = false;
				numTravelBackSteps = 0;
				int ptr = wQueueReadPtr;
				int dir = misc.direction(currentX, currentY, firstX, firstY);
				if (dir != -1 && (dir & 1) != 0) {
					// we can't connect first and current directly
					do {
						lastDir = dir;
						if (--ptr < 0) {
							ptr = walkingQueueSize - 1;
						}
						travelBackX[numTravelBackSteps] = walkingQueueX[ptr];
						travelBackY[numTravelBackSteps++] = walkingQueueY[ptr];
						dir = misc.direction(walkingQueueX[ptr],
								walkingQueueY[ptr], firstX, firstY);
						if (lastDir != dir) {
							found = true;
							break; // either of those two, or a vertex between those is a candidate
						}
					} while (ptr != wQueueWritePtr);
				}
				else {
					found = true;
				}
				if (!found) {
					println_debug("Fatal: couldn't find connection vertex! Dropping packet.");
				}
				else {
					wQueueWritePtr = wQueueReadPtr; // discard any yet unprocessed waypoints from queue
					addToWalkingQueue(currentX, currentY); // have to add this in order to keep consistency in the queue
					if (dir != -1 && (dir & 1) != 0) {
						// need to place an additional waypoint which lies between walkingQueue[numTravelBackSteps-2] and
						// walkingQueue[numTravelBackSteps-1] but can be connected to firstX/firstY
						for (int i = 0; i < numTravelBackSteps - 1; i++) {
							addToWalkingQueue(travelBackX[i], travelBackY[i]);
						}
						int wayPointX2 = travelBackX[numTravelBackSteps - 1], wayPointY2 = travelBackY[numTravelBackSteps - 1];
						int wayPointX1, wayPointY1;
						if (numTravelBackSteps == 1) {
							wayPointX1 = currentX;
							wayPointY1 = currentY;
						}
						else {
							wayPointX1 = travelBackX[numTravelBackSteps - 2];
							wayPointY1 = travelBackY[numTravelBackSteps - 2];
						}
						// we're coming from wayPoint1, want to go in direction wayPoint2 but only so far that
						// we get a connection to first

						// the easiest, but somewhat ugly way:
						// maybe there is a better way, but it involves shitload of different
						// cases so it seems like it isn't anymore
						dir = misc.direction(wayPointX1, wayPointY1, wayPointX2,
								wayPointY2);
						if (dir == -1 || (dir & 1) != 0) {
							println_debug("Fatal: The walking queue is corrupt! wp1=("+ wayPointX1+", "+wayPointY1+"), "+"wp2=("+wayPointX2+", "+wayPointY2+")");
						}
						else {
							dir >>= 1;
							found = false;
							int x = wayPointX1, y = wayPointY1;
							while (x != wayPointX2 || y != wayPointY2) {
								x += misc.directionDeltaX[dir];
								y += misc.directionDeltaY[dir];
								if ((misc.direction(x, y, firstX, firstY) & 1)
										== 0) {
									found = true;
									break;
								}
							}
							if (!found) {
								println_debug("Fatal: Internal error: unable to determine connection vertex!"+" wp1=(" + wayPointX1+", "+wayPointY1+"), wp2=("+wayPointX2+", "+wayPointY2+ "), "+"first=("+firstX+ ", "+firstY+")");
							}
							else {
								addToWalkingQueue(wayPointX1, wayPointY1);
							}
						}
					}
					else {
						for (int i = 0; i < numTravelBackSteps; i++) {
							addToWalkingQueue(travelBackX[i], travelBackY[i]);
						}
					}
					// now we can finally add those waypoints because we made sure about the connection to first
					for (int i = 0; i < newWalkCmdSteps; i++) {
						addToWalkingQueue(newWalkCmdX[i], newWalkCmdY[i]);
					}
				}
				isRunning = newWalkCmdIsRunning || isRunning2;
				for (i = 0; i < playerFollow.length; i++) {
					if (playerFollow[i] != -1
							&& PlayerHandler.getInstance().getPlayer(playerFollow[i]) != null) {
						PlayerHandler.getInstance().getPlayer(playerFollow[i]).postProcessing();
					}
				}
			}
		}
	}

	public void kick() {
		isKicked = true;
	}

	protected int hitDiff = 0;
	protected boolean hitUpdateRequired = false;
	protected boolean IsDead = false;
	protected int NewHP = 10;
	protected boolean SafeMyLife = false;
	protected boolean IsStair = false;
	protected boolean IsDeadTeleporting = false;
	protected boolean IsDeadTimer = false;
	protected void appendHitUpdate2(stream str) {		
		try {
			str.writeByte(hitDiff); // What the perseon got 'hit' for
			if (hitDiff > 0 && newhptype == false && poisonDmg == false) {
				str.writeByteA(1); // 0: red hitting - 1: blue hitting
			}
			else if (hitDiff > 0 && poisonDmg == true) {
				str.writeByteA(2); // 0: red hitting - 1: blue hitting 2: poison 3: orange
			}
			else if (hitDiff > 0 && newhptype == true) {
				str.writeByteA(hptype); // 0: red hitting - 1: blue hitting
			}
			else {
				str.writeByteA(0); // 0: red hitting - 1: blue hitting
			}
			NewHP = (playerLevel[playerHitpoints] - hitDiff);
			if (NewHP <= 0) {
				NewHP = 0;
				IsDead = true;
			}
			str.writeByteC(NewHP); // Their current hp, for HP bar
			str.writeByte(getLevelForXP(playerXP[playerHitpoints])); // Their max hp, for HP bar
			poisonDmg = false;
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}

	protected void appendHitUpdate(stream str) {		
		try {
			str.writeByte(hitDiff);
			if (hitDiff > 0 && newhptype == false && poisondmg == false) {
				str.writeByteS(1);
			}
			else if (hitDiff > 0 && newhptype == false && poisondmg == true) {
				str.writeByteS(2); // 0: red hitting - 1: blue hitting 2: poison 3: orange
			}
			else if (hitDiff > 0 && newhptype == true) {
				str.writeByteS(hptype);
			}
			else if (hitDiff == 0 && newhptype == false && poisondmg == false) {
				str.writeByteS(0);
			}
			NewHP = (playerLevel[playerHitpoints] - hitDiff);
			if (NewHP <= 0) {
				NewHP = 0;
				IsDead = true;
				playerBeingAttacked = false;
			}
			str.writeByte(NewHP);
			str.writeByteC(getLevelForXP(playerXP[playerHitpoints]));
			poisondmg = false;
			newhptype = false;
			hptype = 0;
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}

	public int getLevelForXP(int exp) {
		int points = 0;
		int output = 0;
		for (int lvl = 1; lvl <= 135; lvl++) {
			points += Math.floor((double) lvl + 300.0 * Math.pow(2.0, (double) lvl / 7.0));
			output = (int) Math.floor(points / 4);
			if (output >= exp) {
				return lvl;
			}
		}
		return 0;
	}
	public int animationRequest = -1, animationWaitCycles = 0;
	protected boolean animationUpdateRequired = false;
	public void startAnimation(int animIdx) {
		animationRequest = animIdx;
		animationWaitCycles = 0;
		updateRequired = true; // could lag
	}
	
	public void startAnimation(Animation animation) {
		startAnimation(animation.getValue());
	}

	public void appendAnimationRequest(stream str) {
		str.writeWordBigEndian((animationRequest == -1) ? 65535 : animationRequest);
		str.writeByteC(animationWaitCycles);
	}
	public int playerEmotionReq = -1;
	public int playerEmotionDelay = 0;
	public void appendEmotionUpdate(stream str) {
		str.writeWordBigEndian((playerEmotionReq == -1) ? 65535 : playerEmotionReq);
		str.writeByteC(playerEmotionDelay);
	}
	public int mask1var = 0;
	protected boolean mask1update = false;
	public void appendMask1Update(stream str) {
		str.writeWordBigEndian(mask1var);
	}

	public void faceNPC(int index) {
		faceNPC = index;
		faceNPCupdate = true;
		updateRequired = true;
	}
	protected boolean faceNPCupdate = false;
	public int faceNPC = -1;
	public void appendFaceNPCUpdate(stream str) {
		str.writeWordBigEndian(faceNPC);
	}
	public int mask100var1 = 0;
	public int mask100var2 = 0;
	protected boolean mask100update = false;
	public void appendMask100Update(stream str) {
		str.writeWordBigEndian(mask100var1);
		str.writeDWord(mask100var2);
	}
	public int m4001 = 0;
	public int m4002 = 0;
	public int m4003 = 0;
	public int m4004 = 0;
	public int m4005 = 0;
	public int m4006 = 0;
	public int m4007 = 0;
	protected boolean mask400update = false;
	public void appendMask400Update(stream str) { // Xerozcheez: Something to do with direction
		str.writeByteA(m4001);
		str.writeByteA(m4002);
		str.writeByteA(m4003);
		str.writeByteA(m4004);
		str.writeWordA(m4005);
		str.writeWordBigEndianA(m4006);
		str.writeByteA(m4007); // direction
	}
	public String txt4 = "testing update mask string";
	protected boolean string4UpdateRequired = false;
	public void appendString4(stream str) { // Xerozcheez: Interesting mask, looks like to do with chat
		str.writeString(txt4);
	}

	public void appendDirUpdate2(stream str) {
		str.writeWordBigEndianA(viewToX);
		str.writeWordBigEndian(viewToY);
	}

	public void appendDirUpdate(stream str) {
		if (playerMD != -1) {

			/* str.writeBits(2, 1);		// updateType
			 str.writeBits(3, misc.xlateDirectionToClient[playerMD]);
			 if(updateRequired) {
			 str.writeBits(1, 1);		// tell client there's an update block appended at the end
			 } else {
			 str.writeBits(1, 0);
			 }*/
			str.writeWord(playerMD);
			playerMD = -1;
		}
	}

	// PM Stuff
	public abstract boolean isinpm(long l);

	public abstract void loadpm(long l, int world);

	public abstract void pmupdate(int pmid, int world);
	public int Privatechat = 0;
	public abstract void sendpm(long name, int rights, byte[] chatmessage, int messagesize);
	public long friends[] = new long[200];
	public long ignores[] = new long[100];
	public boolean IsPMLoaded = false;

	public int playerIsMember;
	public int playerMessages;
	public String playerLastConnect;
	public int playerLastLogin;
	public int playerEnergy;
	public int playerEnergyGian;
	public int playerLook[] = new int[6];
	public boolean IsUsingSkill = false;
	public int playerBonus[] = new int[12];
	public int StrPotion = 0;
	public int StrPrayer = 0;
	public int FightType = 1;
	public int playerMaxHit = 0;
	public int playerMaxHitRange = 0;
	public int playerSE = 0x328; // 0x328 SE = Standard Emotion
	public int playerSEW = 0x333; // SEW = Standard Emotion Walking
	public int playerSER = 0x338; // SER = Standard Emotion Run
	public int playerSEA = 0x326; // SEA = Standard Emotion Attack
	public int playerSET = 0x337; // SET = Standard Emotion Turn
	public int playerSET180 = 0x334; // SET180 = Standard Emotion Turn 180
	public int playerSET90WC = 0x335; // SET90WC = Standard Emotion Turn 90
	public int playerSET90CCW = 0x336; // SETCCW90 = Standard Emotion Turn 90
	public int playerMD = -1;
	public int viewToX = -1; 
	public int viewToY = -1;
	protected boolean dirUpdateRequired = false;
	protected boolean dirUpdate2Required = false;
	public boolean IsCutting = false;
	public boolean IsInWilderness = false;
	public boolean IsAttacking = false;
	public boolean IsMining = false;
	public boolean IsAttackingNPC = false;
	public int attacknpc = -1;
	public int npcIndex = -1;
	public int Essence;
	public boolean IsShopping = false;
	public int MyShopID = 0;
	public boolean UpdateShop = false;
	public boolean RebuildNPCList = false;
	public int IsSnowing = 0;
	public int NpcDialogue = 0;
	public int NpcTalkTo = 0;
	public boolean following;
	public boolean NpcDialogueSend = false;
	public int NpcWanneTalk = 0;
	public int WanneTrade = 0;
	public int WanneTradeWith = 0;
	public boolean TradeConfirmed = false;
	public boolean AntiTradeScam = false;
	public int playerFollow[] = new int[500];
	public int playerFollowID = -1; 
	public int DirectionCount = 0;
	public boolean playerAncientMagics = false;
	public String playerServer;
	public int playerGameTime;
	public int playerGameCount;
	public int demonsToKill = 25;
	public int JdemonsToKill = 25;
	
	//public ObjectsDrops objectsdrops = server.objectsdrops;
	public Objects objects = server.objects;
	
	/* Gestion SQL */
	
	public boolean createPlayerSql(String playerName, String playerPass) {
		try {
			String encPassword = Share.getEncodedPassword(playerName+":"+playerPass);
			DBService.getInstance().doExecute("INSERT INTO player_account SET username='"+playerName+"', password='"+encPassword+"';");
			return true;
		}
		catch (SQLException ex) {
			LogService.getInstance().logError("Can't create new player");
			LogService.getInstance().logError(ex.getMessage());
			return false;
		}
	}
	
	public boolean loadPlayerSql () { //charge un joueur
		try {
			ResultSet rs = DBService.getInstance().doQuery("SELECT * FROM player_account WHERE id="+playerSqlID, true);
			
			isMembers = rs.getBoolean("member");
			heightLevel = rs.getInt("heightLevel");
			teleportToX = rs.getInt("posx");
			teleportToY = rs.getInt("posy");
			playerRights = rs.getInt("rights");
			playerEnergy = rs.getInt("energy");
			this.playerSqlID = rs.getInt("id");
			for (int i = 0; i <= 13; i++) {
				playerEquipment[i] = rs.getInt("equip"+i);
				playerEquipmentN[i] = rs.getInt("nequip"+i);
			}

			for (int i = 0; i <= 5; i++)
				playerLook[i] = rs.getInt("look"+i);

			for (int i = 0; i <= 24; i++) {
				playerLevel[i] = rs.getInt("skill"+i);
				playerXP[i] = rs.getInt("skillxp"+i);
			}

			for (int i = 0; i <= 27; i++) {
				if (rs.getInt("item"+i) != -1) {
					ItemTemplate itemTemplate = ItemService.getInstance().getItemTemplate(rs.getInt("item"+i));
					if (itemTemplate != null)
						inventory.setItem(new Item(itemTemplate, rs.getInt("nitem"+i)), i);
					else
						System.out.println("ERREUR LOAD ITEM :"+rs.getInt("item"+i));
					//playerItems[i] = rs.getInt("item"+i) + 1;
					//playerItemsN[i] = rs.getInt("nitem"+i);
				}
			}
			
			pHead = rs.getInt("head");
			pTorso = rs.getInt("torso");
			pArms = rs.getInt("arms");
			pHands = rs.getInt("hands");
			pLegs = rs.getInt("legs");
			pFeet = rs.getInt("feet");
			pBeard = rs.getInt("beard");
			
			updateRequired = true;
			appearanceUpdateRequired = true;
			rs.close();
            return true;
		}
        catch (SQLException ex) {
        	LogService.getInstance().logError(ex.getMessage());
            return false;
		}
	}
	
	public int enterPlayerSQL (String playerName, String playerPass) {
		try {
        	String encPassword = Share.getEncodedPassword(playerName+":"+playerPass);
            ResultSet results = DBService.getInstance().doQuery("SELECT password, id, online FROM player_account WHERE username = '"+playerName+"'");
            if (results.first()) {
	            if (checkBanAccount(results.getInt("id"))) {
	            	results.close();
	                return 4;
	            }
	            else if (checkBanIp(connectedFrom)) {
	            	results.close();
	            	return 4;
	            }
	            else if (results.getString("password").compareTo(encPassword) != 0) {
	            	results.close();
	            	return 3;
	            }
	            else if (results.getBoolean("online")) {
	            	results.close();
	            	return 5;
	            }
	            else {
	            	playerSqlID = results.getInt("id");
	            	results.close();
	            	updatePlayerConnectSql();
	            	return 2;
	            }
            }
            else {
            	if (Config.getInstance().getAutoCreatePlayer()) {
                	if (playerName.length() > 0 && playerPass.length() > 0) {
                        createPlayerSql(playerName, playerPass);
						
                        System.out.println("New Player (auto) :"+playerName);
						
                        results = DBService.getInstance().doQuery("SELECT password, id, online FROM player_account WHERE username = '"+playerName+"'");
                        if (results.first()) {
                        	playerSqlID = results.getInt("id");
                        	updatePlayerConnectSql();
            	            results.close();
            	            return 2;
                        }
                	}
                }
            	return 3;
            }
            //1 : Connecting to...
            //2 : [connection ok]
            //3 : Invalide username or password
            //4 : Your account has been disabled. Please check your message-center for details.
            //5 : Your account is already logged in. Try again in 60 secs...
            //6 : RuneScape has been updated! Please reload this page.
            //7 : This world id full. Please use a different world.
            //8 : Unable to connect. Login server offline
            //9 : Login limit exceeded. Too many connections from your address.
            //10: Unable to connect. Bad session id.
            //11: Connectiong to... Please Try again.
            //12; You need a members account to login to this world. Please subscribe, or use a different world.
            //13: Could not complete login. Please try using a different world.
            //14: The server is being updated. Please wait 1 minute and try again.
        }
        catch (SQLException ex) {
        	LogService.getInstance().logError(ex.getMessage());
            return 11;
        }
	}
	
	public void updatePlayerConnectSql () {
		try {
			DBService.getInstance().doExecute("UPDATE player_account SET online = 1 WHERE id = "+playerSqlID); //TODO derniere date de connection
		}
        catch (SQLException ex) {
        	LogService.getInstance().logError(ex.getMessage());
        }
	}
	
	public void updatePlayerDisconnectSql () {
		try {
			DBService.getInstance().doExecute("UPDATE player_account SET online = 0 WHERE id = "+playerSqlID);
		}
        catch (SQLException ex) {
        	LogService.getInstance().logError(ex.getMessage());
        }
	}
	
	public void loadPlayer2Sql () {
        try {
            ResultSet rs = DBService.getInstance().doQuery("SELECT * FROM player_bank WHERE account="+playerSqlID);
            while (rs.next()) {
            	ItemTemplate itemTemplate = ItemService.getInstance().getItemTemplate(rs.getInt("item"));
				if (itemTemplate != null)
					bank.setItem(new Item(itemTemplate, rs.getInt("nb")), rs.getInt("pos"));
				else
					System.out.println("ERREUR LOAD ITEM :"+rs.getInt("item"));
			}
            rs.close();
        }
        catch (SQLException ex) {
        	LogService.getInstance().logError(ex.getMessage());
        }
		try {
			ResultSet rs = DBService.getInstance().doQuery("SELECT * FROM player_friend WHERE account='"+playerSqlID+"'");
			rs.first();
			while (rs.next()) {
				friends[rs.getInt("slot")] = rs.getLong("friend");
				friendslot = rs.getInt("slot");
				friend64 = rs.getLong("friend");
			}
			rs.close();
			rs = DBService.getInstance().doQuery("SELECT * FROM player_friend WHERE account='"+playerSqlID+"'");
			rs.first();
            while (rs.next()) {
				ignores[rs.getInt("slot")] = rs.getLong("ignore");
			}
            rs.close();
		}
		catch (SQLException ex) {
			LogService.getInstance().logError(ex.getMessage());
        }
	}
	
	public void loadPlayerQuest () {
        try {
            ResultSet rs = DBService.getInstance().doQuery("SELECT * FROM player_quest_status WHERE account="+playerSqlID);
            while (rs.next())
            	quests.put(QuestID.getQuest(rs.getInt("quest")), rs.getInt("status"));
            rs.close();
        }
        catch (SQLException ex) {
        	LogService.getInstance().logError(ex.getMessage());
        }
	}
	
	public void savePlayerSkin() {
		try {
			DBService.getInstance().doExecute("UPDATE player_account SET head="+pHead+",torso="+pTorso+",arms="+pArms+",hands="+pHands+",legs="+pLegs+",feet="+pFeet+",beard="+pBeard+" WHERE id="+playerSqlID+";");
		}
		catch (SQLException ex) {
			LogService.getInstance().logError("can't save player char design.");
			LogService.getInstance().logError(ex.getMessage());
		}
	}
	
	public boolean savePlayerSql () {
		try {
			String sql = "UPDATE player_account SET heightLevel="+heightLevel+",posx="+absX+",posy="+absY+",lastconnection='"+playerLastConnect+"',lastlogin="+playerLastLogin+",";
			for (int i = 0; i <= 13; i++)
				sql = sql + "equip"+i+"="+playerEquipment[i]+",nequip"+i+"="+playerEquipmentN[i]+",";
			
			for (int i = 0; i <= 5; i++)
				sql = sql + "look"+i+"="+playerLook[i]+",";
			
			for (int i = 0; i <= 24; i++)
				sql = sql + "skill"+i+"="+playerLevel[i]+",skillxp"+i+"="+playerXP[i]+",";
			
			for (int i = 0; i <= 27; i++) {
				if (inventory.getItem(i) != null)
					sql = sql + "item" + i + "=" + inventory.getItem(i).getItemTemplate().getId() + ",nitem"+i+"="+inventory.getItem(i).getCount()+",";
				else
					sql = sql + "item"+i+"=-1,nitem"+i+"=0,";
				//sql = sql + "item"+i+"="+(playerItems[i]-1)+",nitem"+i+"="+playerItemsN[i]+",";
			}
			
			sql = sql + "energy="+playerEnergy+" WHERE id="+playerSqlID+";";
			DBService.getInstance().doExecute(sql);
			
			DBService.getInstance().doExecute("DELETE FROM player_bank WHERE account="+playerSqlID+";");
			
			String request = "";
			for (int i = 0; i < Config.MAXBANK; i++) {
				if (bank.getItem(i) != null)
					request += "("+playerSqlID+", "+i+", "+bank.getItem(i).getItemTemplate().getId()+", "+bank.getItem(i).getCount()+"),";
			}
			
			if (request.length() > 0) {
				request = request.substring(0, request.length() - 1);
				DBService.getInstance().doExecute("INSERT INTO player_bank (account, pos, item, nb) VALUES "+request);
			}
			
			DBService.getInstance().doExecute("DELETE FROM player_quest_status WHERE account="+playerSqlID+";");
			request = "";
			for(Entry<QuestID, Integer> entry : quests.entrySet())
				request += "("+entry.getKey().getValue()+","+playerSqlID+","+entry.getValue()+"),";
			
			if (request.length() > 0) {
				request = request.substring(0, request.length() - 1);
				DBService.getInstance().doExecute("INSERT INTO player_quest_status (quest, account, status) VALUES "+request);
			}
			
			return true;
		}
		catch (SQLException ex) {
			LogService.getInstance().logError("Can't save player : "+playerName+" ("+playerId+")");
			LogService.getInstance().logError(ex.getMessage());
			return false;
		}
	}
	
	public void updateBanPlayer (String playerName, boolean ban) {//TODO travailler avec l'id de compte
		try {
			if (ban) {
				DBService.getInstance().doExecute("UPDATE player_account set banned = 1 WHERE username='"+playerName+"'");
				System.out.println("Joueur "+playerName+" banni");
			}
			else {
				DBService.getInstance().doExecute("UPDATE player_account set banned = 0 WHERE username='"+playerName+"'");
				System.out.println("Joueur "+playerName+" de-banni");
			}
		}
		catch (SQLException ex) {
			LogService.getInstance().logError("Can't update player ban");
			LogService.getInstance().logError(ex.getMessage());
		}
	}
	
	public boolean checkBanIp (String ip) {
		try {
            Calendar cal = new GregorianCalendar();
            int bandate = (cal.get(Calendar.YEAR) * 10000) + (cal.get(Calendar.MONTH) * 100) + cal.get(Calendar.DAY_OF_MONTH);

            DBService.getInstance().doExecute("DELETE FROM ip_banned WHERE unbandate < "+bandate+" AND bandate != unbandate");
            ResultSet rs = DBService.getInstance().doQuery("SELECT count(*) as nb FROM ip_banned WHERE ip='"+ip+"'");
			rs.first();
			if (rs.getInt("nb") > 0) {
				return true;
			}
		}
		catch (SQLException ex) {
			LogService.getInstance().logError(ex.getMessage());
		}
        return false;
	}
	
	public boolean checkBanAccount (int id) {
		try {
            Calendar cal = new GregorianCalendar();
            int bandate = (cal.get(Calendar.YEAR) * 10000) + (cal.get(Calendar.MONTH) * 100) + cal.get(Calendar.DAY_OF_MONTH);

            DBService.getInstance().doExecute("DELETE FROM account_banned WHERE unbandate < "+bandate+" AND bandate != unbandate");
            ResultSet rs = DBService.getInstance().doQuery("SELECT count(*) as nb FROM account_banned WHERE id="+id);
			rs.first();
			if (rs.getInt("nb") > 0) {
				return true;
			}
		}
		catch (SQLException ex) {
			LogService.getInstance().logError(ex.getMessage());
		}
        return false;
	}

	/* gestion Trade */
	
	public void resetTrade () {
		tradeWith = 0;
		tradeWaitingTime = 0;
		tradeStatus = 0;
		tradeUpdateOther = false;
		tradeOtherDeclined = false;
		WanneTrade = 0;
		actionTimer = 10;
		WanneTradeWith = 0;
		TradeConfirmed = false;
		for (int i = 0; i < playerTItems.length; i++) {
			playerTItems[i] = 0;
			playerTItemsN[i] = 0;
			playerOTItems[i] = 0;
			playerOTItemsN[i] = 0;
		}
	}
	
	public void checkTradeArea() {
		if (absX >= 3197 && absX <= 3229 && absY >= 3199 && absY <= 3238) {
			InTradeArea = true;
		} else {
			InTradeArea = false;
		}
	}
	
	public int freeTradeSlots() {
		int freeS = 0;

		for (int i = 0; i < playerTItems.length; i++) {
			if (playerTItems[i] <= 0) {
				freeS++;
			}
		}
		return freeS;
	}
	
	public Inventory getInventory() { return inventory; }
	public int GetPlayerRights () { return playerRights; }
	public String getPlayerPass () { return playerPass; }
	public void setPlayerPass (String pass) { playerPass = pass; }
	public int getAbsX () { return absX; }
	public int getAbsY () { return absY; }
	public int getAbsH () { return heightLevel; }
    public String getPlayerName () { return playerName; }
    public int getSkillLevel(SkillType skillType) {
    	return getLevelForXP(playerXP[skillType.getValue()]);
    }
    
    public void sendPlayerMessage(String message) {
    	((client)this).sendMessage(message);
    }
	
	public void setTeleport (int x, int y, int h) {
		teleportToX = x;
		teleportToY = y;
		heightLevel = h;
	}
	
	protected boolean isGoodDistance(int absX, int absY, int distanceMax) {
		return Math.abs(this.absX - absX) <= distanceMax && Math.abs(this.absY - absY) <= distanceMax;
	}
	
	private int getOldIndexEquipement(ItemType itemType) {
		switch (itemType) {
			case HELM:
				return 0;
			case CLOAK:
				return 1;
			case NECK:
				return 2;
			case HAND:
				return 3;
			case BODY:
				return 4;
			case SHIELD:
				return 5;
			case LEGS:
				return 7;
			case GLOVE:
				return 9;
			case FOOT:
				return 10;
			case RING:
				return 12;
			case ARROW:
				return 13;
			default:
				return -1;
		}
	}
	
	protected void wearItem(int itemID) {
		if (!inventory.hasItem(itemID))
			return ;
		ItemTemplate item = ItemService.getInstance().getItemTemplate(itemID);
		if (item == null)
			return ;
		ItemType type = item.getWearType();
		if (type == null)
			return ;
		
		int index = getOldIndexEquipement(type);
		if (playerEquipment[index] != -1) {
			if (!inventory.haveSpaceForItem(playerEquipment[index], playerEquipmentN[index]))
				return ;
			inventory.addItem(playerEquipment[index], playerEquipmentN[index]);
		}
		playerEquipment[index] = itemID;
		playerEquipmentN[index] = 1;
		inventory.removeItem(itemID, 1);
		((client)this).updateWearItem(index, item.getClientId(), 1);
		updateEquipedBonus();
		
		/*
		//public boolean wear(int wearID, int slot) {
			int targetSlot = 0;

			if (slot == playerWeapon && duelRule[4]) {
				sendMessage("Weapons are disabled in this duel!");
			}
			if ((slot == playerHat || slot == playerCape || slot == playerChest
					|| slot == playerLegs || slot == playerShield
					|| slot == playerFeet)
					&& duelRule[5]) {
				sendMessage("Armour is disabled in this duel!");
			}
			if ((playerItems[slot] - 1) == wearID) {
				if (wearID == 6070) {
					npcId = 1645;
					isNpc = true;
					updateRequired = true;
					appearanceUpdateRequired = true;
				}
				resetItems(3214); // THIS MIGHT STOP CLIENT HACKS HMM?
				int ID = 0;

				targetSlot = itemType(wearID);
				int CLHerblore = GetCLHerblore(wearID);
				int CLAttack = GetCLAttack(wearID);
				int CLAgility = GetCLAgility(wearID);
				int CLCooking = GetCLCooking(wearID);
				int CLDefence = GetCLDefence(wearID);
				int CLStrength = GetCLStrength(wearID);
				int CLMagic = GetCLMagic(wearID);
				int CLFletching = GetCLFletching(wearID);
				int CLRanged = GetCLRanged(wearID);
				int CLPrayer = GetCLPrayer(wearID);
				int CLSlayer = GetCLSlayer(wearID);
				int CLFiremaking = GetCLFiremaking(wearID);
				int CLCrafting = GetCLCrafting(wearID);
				int CLRunecrafting = GetCLRunecrafting(wearID);
				int CLMining = GetCLMining(wearID);
				int CLSmithing = GetCLSmithing(wearID);
				int CLFishing = GetCLFishing(wearID);
				int CLWoodcutting = GetCLWoodcutting(wearID);
				int CLThieving = GetCLThieving(wearID);
				
				// int CLMembers = server.ItemRequirements.getMembers(wearID, ID);
				int CLHitpoints = GetCLHitpoints(wearID);
				boolean GoFalse = false;

				if (targetSlot == playerWeapon && item2handed(wearID)
						&& playerEquipment[playerShield] != -1
						&& playerEquipment[playerWeapon] != -1 && freeSlots() < 1) {
					GoFalse = true;
					sendMessage("Not enough space in your inventory.");
				}
				if (getLevelForXP(playerXP[0]) - CLAttack < 0) {
					sendMessage(
							"You need " + CLAttack + " " + statName[playerAttack]
							+ " to equip this item.");
					GoFalse = true;
				}
				if (getLevelForXP(playerXP[8]) - CLWoodcutting < 0) {
					sendMessage(
							"You need " + CLWoodcutting + " "
							+ statName[playerWoodcutting] + " to equip this item.");
					GoFalse = true;
				}
				if (getLevelForXP(playerXP[1]) - CLDefence < 0) {
					sendMessage(
							"You need " + CLDefence + " " + statName[playerDefence]
							+ " to equip this item.");
					GoFalse = true;
				}
				if (getLevelForXP(playerXP[14]) - CLMining < 0) {
					sendMessage(
							"You need " + CLMining + " " + statName[playerMining]
							+ " to equip this item.");
					GoFalse = true;
				}
				if (getLevelForXP(playerXP[9]) - CLFletching < 0) {
					sendMessage(
							"You need " + CLFletching + " "
							+ statName[playerFletching] + " to equip this item.");
					GoFalse = true;
				}
				if (getLevelForXP(playerXP[10]) - CLFishing < 0) {
					sendMessage(
							"You need " + CLFishing + " " + statName[playerFishing]
							+ " to equip this item.");
					GoFalse = true;
				}
				if (getLevelForXP(playerXP[18]) - CLSlayer < 0) {
					sendMessage(
							"You need " + CLSlayer + " " + statName[playerSlayer]
							+ " to equip this item.");
					GoFalse = true;
				}
				if (getLevelForXP(playerXP[16]) - CLAgility < 0) {
					sendMessage(
							"You need " + CLAgility + " " + statName[playerAgility]
							+ " to equip this item.");
					GoFalse = true;
				}
				if (getLevelForXP(playerXP[7]) - CLCooking < 0) {
					sendMessage(
							"You need " + CLCooking + " " + statName[playerCooking]
							+ " to equip this item.");
					GoFalse = true;
				}
				if (getLevelForXP(playerXP[11]) - CLFiremaking < 0) {
					sendMessage(
							"You need " + CLFiremaking + " "
							+ statName[playerFiremaking] + " to equip this item.");
					GoFalse = true;
				}
						   
				if (getLevelForXP(playerXP[12]) - CLCrafting < 0) {
					sendMessage(
							"You need " + CLCrafting + " "
							+ statName[playerCrafting] + " to equip this item.");
					GoFalse = true;
				}
				if (getLevelForXP(playerXP[20]) - CLRunecrafting < 0) {
					sendMessage(
							"You need " + CLRunecrafting + " "
							+ statName[playerRunecrafting] + " to equip this item.");
					GoFalse = true;
				}
				if (getLevelForXP(playerXP[15]) - CLHerblore < 0) {
					sendMessage(
							"You need " + CLHerblore + " "
							+ statName[playerHerblore] + " to equip this item.");
					GoFalse = true;
				}			
				if (getLevelForXP(playerXP[2]) - CLStrength < 0) {
					sendMessage(
							"You need " + CLStrength + " "
							+ statName[playerStrength] + " to equip this item.");
					GoFalse = true;
				}   
				if (getLevelForXP(playerXP[13]) - CLSmithing < 0) {
					sendMessage(
							"You need " + CLSmithing + " "
							+ statName[playerSmithing] + " to equip this item.");
					GoFalse = true;
				}					 
				if (getLevelForXP(playerXP[6]) - CLMagic < 0) {
					sendMessage(
							"You need " + CLMagic + " " + statName[playerMagic]
							+ " to equip this item.");
					GoFalse = true;
				}					  
				if (getLevelForXP(playerXP[4]) - CLRanged < 0) {
					sendMessage(
							"You need " + CLRanged + " " + statName[playerRanged]
							+ " to equip this item.");
					GoFalse = true;
				}
				if (getLevelForXP(playerXP[17]) - CLThieving < 0) {
					sendMessage(
							"You need " + CLThieving + " "
							+ statName[playerThieving] + " to equip this item.");
					GoFalse = true;
				}
				if (getLevelForXP(playerXP[5]) - CLPrayer < 0) {
					sendMessage(
							"You need " + CLPrayer + " " + statName[playerPrayer]
							+ " to equip this item.");
					GoFalse = true;
				}
				if (getLevelForXP(playerXP[3]) - CLHitpoints < 0) {
					sendMessage(
							"You need " + CLHitpoints + " "
							+ statName[playerHitpoints] + " to equip this item.");
					GoFalse = true;
				}
				if (GoFalse)
					return false;
				int wearAmount = playerItemsN[slot];

				if (wearAmount < 1)
					return false;
				wearing = true;
				if (slot >= 0 && wearID >= 0) {
					deleteItem(wearID, slot, wearAmount);
					if (playerEquipment[targetSlot] != wearID
							&& playerEquipment[targetSlot] >= 0) {
						addItem(playerEquipment[targetSlot],
								playerEquipmentN[targetSlot]);
						resetItems(3214); // THIS MIGHT STOP CLIENT HACKS HMM?
					} else if (Item0.itemStackable[wearID]
							&& playerEquipment[targetSlot] == wearID) {
						wearAmount = playerEquipmentN[targetSlot] + wearAmount;
					} else if (playerEquipment[targetSlot] >= 0) {
						addItem(playerEquipment[targetSlot],
								playerEquipmentN[targetSlot]);
						resetItems(3214); // THIS MIGHT STOP CLIENT HACKS HMM?
					}
				}
				outStream.createFrameVarSizeWord(34);
				outStream.writeWord(1688);
				outStream.writeByte(targetSlot);
				outStream.writeWord(wearID + 1);
				if (wearAmount > 254) {
					outStream.writeByte(255);
					outStream.writeDWord(wearAmount);
				} else {
					outStream.writeByte(wearAmount); // amount	
				}
				outStream.endFrameVarSizeWord();
				playerEquipment[targetSlot] = wearID;
				playerEquipmentN[targetSlot] = wearAmount;
				if (item2handed(wearID) && playerEquipment[playerShield] != -1) {
					remove(playerEquipment[playerShield], playerShield);
				}
				if (targetSlot == playerShield
						&& item2handed(playerEquipment[playerWeapon])
						&& playerEquipment[playerWeapon] != -1) {
					remove(playerEquipment[playerWeapon], playerWeapon);
				}
				if (targetSlot == playerWeapon) {
					SendWeapon(wearID, GetItemName(wearID));
					playerSE = GetStandAnim(wearID);
					playerSEW = GetWalkAnim(wearID);
					playerSER = GetRunAnim(wearID);
					playerSEA = 0x326;
					if (wearID == 4747) { // Torag Hammers
						playerSEA = 0x814;
					}
					if (wearID == 4151) { // Whip
						playerSEW = 1660;
						playerSER = 1661;
					}
					pEmote = playerSE;
				}
				ResetBonus();
				GetBonus();
				WriteBonus();
				SendWeapon((playerEquipment[playerWeapon]),
						GetItemName(playerEquipment[playerWeapon]));
				updateRequired = true;
				appearanceUpdateRequired = true;
				wearing = false;
				return true;
			}
			return false;
		}
		*/
	}
	
	protected void removeWearItem(int itemID, int slot) {
		if (playerEquipment[slot] != itemID || playerEquipmentN[slot] <= 0) 
			return ;
		
		if (!inventory.haveSpaceForItem(playerEquipment[slot], playerEquipmentN[slot])) {
			sendPlayerMessage("You doesn't have enough place in your inventory.");
			return ;
		}
		
		inventory.addItem(playerEquipment[slot], playerEquipmentN[slot]);
		playerEquipment[slot] = -1;
		playerEquipmentN[slot] = 0;
		resetAnimation();
		((client)this).removeWearItem(slot);
		updateEquipedBonus();
		
		/*
		if (addItem(playerEquipment[slot], playerEquipmentN[slot])) {
			if (wearID == 6070) {
				npcId = 0;
				isNpc = false;
				updateRequired = true;
				appearanceUpdateRequired = true;
			}
			resetItems(3214); // THIS MIGHT STOP CLIENT HACKS HMM?
			playerEquipment[slot] = -1;
			playerEquipmentN[slot] = 0;
			outStream.createFrame(34);
			outStream.writeWord(6);
			outStream.writeWord(1688);
			outStream.writeByte(slot);
			outStream.writeWord(0);
			outStream.writeByte(0);
			ResetBonus();
			GetBonus();
			playerIndex = -1;
			
			pEmote = playerSE;
			WriteBonus();
			if (slot == playerWeapon) {
				SendWeapon(-1, "Unarmed");
			}
			SendWeapon((playerEquipment[playerWeapon]),
					GetItemName(playerEquipment[playerWeapon]));
			updateRequired = true;
			appearanceUpdateRequired = true;
		}
		*/
	}
	
	public void updateEquipedBonus() {
		for (int i = 0; i < 12; i++)
			playerBonus[i] = 0;
		for (int i = 0; i < 14; i++) {
			if (playerEquipment[i] == -1)
				continue;
			ItemTemplate item = ItemService.getInstance().getItemTemplate(playerEquipment[i]);
			if (item == null)
				continue;
			for (int b = 0; b < 12; b++)
				playerBonus[b] += item.getBonus(b);
		}
		
		int offset = 0;
		String send = "";
		for (int i = 0; i < 12; i++) {
			if (playerBonus[i] >= 0)
				send = BonusName[i] + ": +" + playerBonus[i];
			else
				send = BonusName[i] + ": -" + java.lang.Math.abs(playerBonus[i]);
			
			if (i == 10)
				offset = 1;
			((client)this).sendFrame126(send, (1675 + i + offset));
		}
	}
}
